package com.hup.utils.io.socket.netty;

import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.commons.start.StartupInfo;
import com.hup.utils.commons.swing.SimpleTestFrame;
import com.hup.utils.commons.swing.SwingExecutable;
import com.hup.utils.io.netty.NettyTCPClient;
import com.hup.utils.io.netty.NettyTCPClient.ClientCallback;
import com.hup.utils.io.netty.NettyTCPClient.ClientConfig;
import com.hup.utils.io.netty.annotation.DataHandlerMethod;
import com.hup.utils.io.netty.handler.DataHandler;
import com.hup.utils.io.netty.handler.HeartbeatHandler.IdleCallback;
import com.hup.utils.io.netty.handler.LoginResponseHandler;
import com.hup.utils.io.netty.model.BaseDataPacket;
import com.hup.utils.io.netty.model.LoginRequestPacket;
import com.hup.utils.io.netty.model.LoginResponsePacket;
import com.hup.utils.io.socket.netty.UDPBroadcastTest.BroadcastReceiver;
import com.hup.utils.io.socket.netty.model.MyLoginData;
import com.hup.utils.io.socket.netty.model.ScreenDataDto;
import com.hup.utils.io.socket.netty.model.TestImageData;
import com.hup.utils.io.socket.netty.model.TestStringData;
import com.hup.utils.io.socket.netty.view.ClientTestForm;
import com.hup.utils.io.socket.netty.view.ScreenFrame;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.FrameUtil;
import com.hup.utils.swing.FrameUtil.Gravity;
import com.hup.utils.swing.ImageUtil;
import io.netty.channel.Channel;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 客户端测试类
 *
 * @author hugan
 * @date 2021/4/25
 */
@Log4j2
public class ClientDemo {
    private static final boolean onTop = true;
    //private static final String DEFAULT_HOST = "192.168.1.100";
    private static final String DEFAULT_HOST = "127.0.0.1";
    //private static final String DEFAULT_HOST = "192.168.1.128";
    private static final String DEFAULT_PORT = "8887";
    private static final int FRAME_COUNT = 1;
    //private static final int FRAME_COUNT = 3;

    private static final boolean enableBroadcast = false;
    //private static final boolean enableBroadcast = true;

    public static void main(String[] args) {
        StartupInfo.init();
        LookThemeUtil.initNormal(null);
        SwingExecutable.initSwingExecutor();

        JFrame frame = null;
        for (int i = 0; i < FRAME_COUNT; i++) {
            frame = new ClientDemo(new SimpleTestFrame(null)).showFrame(frame);
        }
    }

    private final SimpleTestFrame frame;
    private ClientTestForm form;
    private ClientConfig clientConfig;
    private NettyTCPClient client;
    private BroadcastReceiver broadcastReceiver;
    private ScreenFrame screenFrame;

    public ClientDemo(SimpleTestFrame frame) {
        this.frame = frame;
        initView();
        initListener();
        initClient();
    }

    /**
     * @param previous 多实例启动时,上个窗口
     */
    public JFrame showFrame(JFrame previous) {
        if (previous != null) {
            frame.setLocation(previous.getX(), previous.getY() + 150);
        }
        frame.setVisible(true);
        return frame;
    }

    private void initView() {
        frame.setTitle(ClientDemo.class.getSimpleName());
        frame.setSize(500, 300);
        frame.setAlwaysOnTop(onTop);
        FrameUtil.setLocation(frame, Gravity.TOP_R);
        frame.setLocation(frame.getX(), 130);
        form = new ClientTestForm();
        frame.setContentPane(form.root);
        form.txtHost.setText(DEFAULT_HOST);
        form.txtPort.setText(DEFAULT_PORT);
        onConnectStateChange(false);

        screenFrame = new ScreenFrame();
    }

    private void initListener() {
        form.btnStart.addActionListener(e -> {
            ((JComponent) e.getSource()).setEnabled(false);
            updateConfigByView();
            client.connect();
        });
        form.btnStop.addActionListener(e -> {
            ((JComponent) e.getSource()).setEnabled(false);
            client.disconnect();
        });
        form.btnSendStr.addActionListener(e -> {
            TestStringData testStringData = new TestStringData(form.txtInput.getText());
            client.send(testStringData);
        });
        form.btnSendImage.addActionListener(e -> {
            ImageIcon imageIcon = new ImageIcon(getClass().getResource("/img/ali_jump.gif"));
            client.send(new TestImageData(imageIcon));
        });
        form.btnScreenFrame.addActionListener(e -> screenFrame.setVisible(true));
    }

    private void initClient() {
        clientConfig = new ClientConfig(new ClientCallback() {
            @Override
            public void onConnectResult(boolean success, String host, Integer port, LoginResponsePacket loginResponse) {
                onConnectStateChange(success);
                appendLog(host + ":" + port + "连接" + (success ? "成功," : "失败,") + loginResponse);
                if (success) {
                    screenFrame.setVisible(true);
                    switchUDPBroadcast(false);
                }
            }

            @Override
            public void onConnectionLost() {
                onConnectStateChange(false);
                screenFrame.setSubTitle("已断开");
                appendLog("连接已断开");
                switchUDPBroadcast(true);
            }

            @Override
            public void onUnknownData(BaseDataPacket data) {
                appendLog(data.toString());
            }
        });
        clientConfig.addDataHandler(new MyDataHandler());
        clientConfig.setAddHeartbeat(true, new IdleCallback() {
        });
        client = new NettyTCPClient(clientConfig);
        switchUDPBroadcast(true);
    }

    private void switchUDPBroadcast(boolean isOn) {
        if (!enableBroadcast) return;
        log.info("isOn={}", isOn);
        if (isOn) {
            try {
                broadcastReceiver = new BroadcastReceiver();
                broadcastReceiver.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            if (broadcastReceiver != null) {
                broadcastReceiver.doStop();
                broadcastReceiver = null;
            }
        }
    }

    private void updateConfigByView() {
        try {
            clientConfig.target(form.txtHost.getText(), Integer.parseInt(form.txtPort.getText()));
            clientConfig.loginHandler(!form.cbLoginValid.isSelected() ? null : new LoginResponseHandler<LoginRequestPacket, LoginResponsePacket>() {
                @Override
                public Class<LoginResponsePacket> getLoginResponseType() {
                    return LoginResponsePacket.class;
                }

                @Override
                public LoginRequestPacket getLoginValidData() {
                    MyLoginData loginData = new MyLoginData("用户" + ThreadLocalRandom.current().nextInt(100, 500));
                    appendLog("发送登录认证请求:" + loginData.name);
                    screenFrame.setSubTitle(loginData.name);
                    return loginData;
                }
            });
        } catch (Exception e) {
            AppExceptionHandler.handle("更新配置异常:", e);
            appendLog("更新配置异常:" + e.getMessage());
        }
    }

    private void appendLog(String msg) {
        log.info("{}", msg);
        ComponentUtil.appendAndScroll(form.txtContent, msg + "\n");
    }

    private void onConnectStateChange(boolean connected) {
        form.btnStart.setEnabled(!connected);
        form.btnStop.setEnabled(connected);
    }

    public class MyDataHandler implements DataHandler {
        @DataHandlerMethod
        public void handleString(TestStringData data, Channel channel) {
            appendLog(channel.remoteAddress().toString() + ":" + data.msg);
        }


        @DataHandlerMethod
        public void handleString(ScreenDataDto data, Channel channel) {
            //appendLog(channel.remoteAddress().toString() + ":" + data);
            Image image = new ImageIcon(data.getData()).getImage();
            if (image != null) {
                BufferedImage bufferedImage = ImageUtil.getImage(image);
                log.info("w={}, h={}", bufferedImage.getWidth(), bufferedImage.getHeight());
                screenFrame.setImage(bufferedImage);
            }
        }

    }

}
